﻿#Region "Copyright/License"

'  Copyright (C) 2011 Paycircuit.com, Thinking Spot
' 
'  Licensed under the Apache License, Version 2.0 (the "License");
'  you may not use this file except in compliance with the License.
'  You may obtain a copy of the License at
' 
'       http://www.apache.org/licenses/LICENSE-2.0
' 
'  Unless required by applicable law or agreed to in writing, software
'  distributed under the License is distributed on an "AS IS" BASIS,
'  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
'  See the License for the specific language governing permissions and
'  limitations under the License.

#End Region

Imports System.ComponentModel.DataAnnotations
Imports System.Runtime.Serialization

Namespace google.iap

    ''' <summary>
    ''' Google In-App Payments specific JWT implementation
    ''' </summary>
    ''' <remarks>https://sites.google.com/site/inapppaymentsapi/home</remarks>
    <DataContract()>
    Public Class InAppItemObject
        Inherits JWTClaim


#Region "Notes"
        'Order Property of DataMember is used (only) for easier unit testing with Google's demo page. There is no "required order".
        'Otherwise, the default datacontract datamember order is alphabetical making it impossible to test using Google demo page.
        'Google testing/demo page: https://sandbox.google.com/checkout/customer/gadget/inapp/demo.html
#End Region

#Region "Defaults"

        Private _typ As String = "google/payments/inapp/item/v1"
        Private _aud As String = "Google"


        ''' <summary>
        ''' Note the adjustment of -1 minute to compensate for differences between system times when generating iat, adjust as necessary. 
        ''' If your system clock is faster than Google's, your JWT will be rejected (jwt spec: the current date/time MUST be after the issued date/time listed in the iat claim)
        ''' Also note leap seconds in Windows vs nix.
        ''' </summary>
        ''' <remarks>Sorry #rapture fans: If the world still exists on 01/19/2038, this is an issue (Integer)</remarks>
        Private _iat As Integer = JWTHelpers.myServerClock() - 60

#End Region

        ''' <summary>
        ''' Required. Issuer Claim. Your Merchant/Seller ID. 
        ''' On Postback from Google: This will be set by Google to "Google", and will be verified (case-sensitive). Adjust callback handler as needed.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        <Required()>
        <DataMember(isRequired:=True)>
        Public Overrides Property iss As String = String.Empty

        ''' <summary>
        ''' Required. Audience Claim. Default is "Google". Provide value for future api version changes.
        ''' On Postback from Google: This will be set by Google to your  Merchant/Seller ID and will be verified. Adjust callback handler as needed
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        <Required()>
        <DataMember(isRequired:=True)>
        Public Overrides Property aud As String
            Get
                Return _aud
            End Get
            Set(value As String)
                _aud = value
            End Set
        End Property

        ''' <summary>
        ''' Required. Type Claim. Default is "google/payments/inapp/item/v1". Provide value for future api version changes.
        ''' On  Postback from Google: This will be set by Google and will be verified. Currently "google/payments/inapp/item/v1/postback/buy". Adjust callback handler as needed.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        <Required()>
        <DataMember(isRequired:=True)>
        Public Overrides Property typ As String
            Get
                Return _typ
            End Get
            Set(value As String)
                _typ = value
            End Set
        End Property

        ''' <summary>
        ''' Required. Issued At Claim. The time when the JWT was issued.
        ''' Specified in number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the desired date/time.
        ''' Default value is current server time with a clock adjustment of -1 minute.
        ''' On Postback from Google: Google will set this value and will be verified.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        <Required()>
        <DataMember(isRequired:=True)>
        Public Overrides Property iat As Integer
            Get
                Return _iat
            End Get
            Set(value As Integer)
                _iat = value
            End Set
        End Property

        ''' <summary>
        ''' Optional. Expiration claim. Must be greater than issued at (iat) and maximum of 1 hour after issued at (iat).
        ''' Specified in number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the desired date/time.
        ''' On Postback from Google: Google will set this value and will be verified.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        <DataMember(EmitDefaultValue:=False, isRequired:=False)>
        Public Overrides Property exp As Integer?


        ''' <summary>
        ''' Required. Instance of InAppItemRequestObject.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        <Required()>
        <DataMember(isRequired:=True, Order:=1)>
        Public Property request As InAppItemRequestObject = Nothing

        ''' <summary>
        ''' Used only on Google Postback
        ''' Contains orderID
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        <DataMember(EmitDefaultValue:=False, isRequired:=False)>
        Public Property response As InAppItemResponseObject = Nothing

        ''' <summary>
        ''' When using this constructor, refer to property Intellisense documentation/information/hints for requirements.
        ''' Refer to Google documentation.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub New()

        End Sub

        Public Sub New(ByVal request As InAppItemRequestObject)
            Me.request = request
        End Sub

        ''' <summary>
        ''' This constructor initializes a new InAppItemRequestObject.
        ''' Text values exceeding maximum length requirements will be truncated. 
        ''' Use named arguments for optional parameters if you don't provide all.
        ''' </summary>
        ''' <param name="name50">Required (name). Maximium of 50 characters (excess truncated).</param>
        ''' <param name="description100">Optional (description). Pass null, Nothing or String.Empty to prevent serialization of this property. Maximum of 100 characters (excess truncated).</param>
        ''' <param name="price">Required (price). Up to 2 decimal places only. Google will reject value exceeding 2 decimal places.</param>
        ''' <param name="isoCurrency3">Required (isoCurrencyCode). 3 letter ISO Currency. e.g. USD (excess truncated).</param>
        ''' <param name="sellerData200">Optional (sellerData). Pass null, Nothing or String.Empty to prevent serialization of this property. Maximum of 200 characters (excess truncated). </param>
        ''' <param name="sellerId">Required (iss). Your Seller ID when building your JWT (you are issuer). On postback, Google is issuer and will set this value ("Google").</param>
        ''' <param name="expMin60">Optional (exp). Maximium value is 60 (minutes from iat). Pass null or Nothing to prevent serialization of this property.</param>
        ''' <param name="optionalAud">This is an optional parameter. Use only to override default aud value ("Google").</param>
        ''' <param name="optionalTyp">This is an optional parameter. Use only to override default typ value ("google/payments/inapp/item/v1").</param>
        ''' <param name="optionalIat">This is an optional parameter. Use only to override default iat value (current UTC time in number of seconds from Unix epoch with skew of -60 secs).</param>
        ''' <remarks>Google Sites reference: http://sites.google.com/site/inapppaymentsapi/reference </remarks>
        Public Sub New(ByVal name50 As String, ByVal description100 As String, ByVal price As String, ByVal isoCurrency3 As String,
                       ByVal sellerData200 As String, ByVal sellerId As String, ByVal expMin60 As Integer?,
                       Optional ByVal optionalAud As String = "",
                       Optional ByVal optionalTyp As String = "",
                       Optional ByVal optionalIat As Integer = 0)

            If Not String.IsNullOrEmpty(name50) AndAlso IsNumeric(price) AndAlso Not String.IsNullOrEmpty(isoCurrency3) AndAlso Not String.IsNullOrEmpty(sellerId) Then
                Me.request = New InAppItemRequestObject()
                Me.request.name = If(name50.Length > 50, name50.Substring(0, 50), name50)

                If Not String.IsNullOrEmpty(description100) Then
                    Me.request.description = If(description100.Length > 100, description100.Substring(0, 100), description100)
                End If

                Me.request.price = price

                Me.request.currencyCode = If(isoCurrency3.Length > 3, isoCurrency3.Substring(0, 3), isoCurrency3)

                If Not String.IsNullOrEmpty(sellerData200) Then
                    Me.request.sellerData = If(sellerData200.Length > 200, sellerData200.Substring(0, 200), sellerData200)
                End If

                If expMin60.HasValue AndAlso expMin60.Value > 0 Then
                    expMin60 = CInt(If(expMin60.Value <= 60, expMin60.Value, 60))
                    Me._exp = Me.iat + (expMin60.Value * 60)
                End If

                Me._iss = sellerId

                If Not String.IsNullOrEmpty(optionalAud) Then
                    Me._aud = optionalAud
                End If

                If Not String.IsNullOrEmpty(optionalTyp) Then
                    Me._typ = optionalTyp
                End If

                If optionalIat > 0 Then
                    Me._iat = optionalIat
                End If

            Else
                Throw New ArgumentNullException("Invalid InAppItemObject", "You must provide required arguments.")
            End If
        End Sub

        <OnDeserialized()>
        Public Sub validateDeserializedClaim(context As System.Runtime.Serialization.StreamingContext)
            validateClaim()
        End Sub

        <OnSerialized()>
        Public Sub validateSerializedClaim(context As System.Runtime.Serialization.StreamingContext)
            validateClaim()
        End Sub

        Private Sub validateClaim()
            If Me.exp.HasValue Then
                Dim _issued As Integer = Me.iat
                Dim _expiration As Integer = Me.exp.Value
                If _expiration <= 0 OrElse _expiration <= _issued OrElse _expiration > _issued + 3600 Then
                    Throw New ArgumentOutOfRangeException("InAppItemObject exp", "Expiration time claim (exp) must be greater than issued at claim (iat) and maximum of 1 hour after issued at claim (iat)")
                End If
            End If
            If String.IsNullOrEmpty(Me.iss) OrElse String.IsNullOrEmpty(Me.aud) OrElse String.IsNullOrEmpty(Me.typ) OrElse Me.iat <= 0 OrElse Me.request Is Nothing Then
                Throw New NullReferenceException("Invalid InAppItemObject - make sure you provide all required properties.")
            End If
        End Sub

    End Class

End Namespace